import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 全量字段映射生成器<br>
 * 主要是将代码生成工具or插件所生成的数据库表映射实体类再转换一份成所需要的全量字段pojo类，比如dto或者vo。加上lombok和swagger相关的注解<br>
 * 使用时将代码拷贝到项目目录下(与启动类放同一个文件夹下)，然后修改对应配置<br>
 * @author dalwin
 */
public class PoJoGenerator {

    //region 配置区域
    private static final String TARGET_PACKAGE_PREFIX = "com.iktapp.api.dto.humanresource"; // pojo类包名前缀
    private static final String SOURCE_DIR = "/src/main/java/com/xxx/api/entity"; // entity源代码路径，使用相对路径
    private static final String TARGET_DIR = "/src/main/java/com/xxx/api/dto"; //  目标代码路径，使用相对路径
    private static final List<String> TARGET_ENTITY_NAMES = Arrays.asList("ExchangeRecord", "Post", "Resume", "ResumeExperience"); // 需要转换的entity实体类名称
//    private static final List<String> TARGET_ENTITY_NAMES = Arrays.asList("Post", "Resume"); // 需要转换的entity实体类名称
//    private static final List<String> TARGET_ENTITY_NAMES = Collections.singletonList("ExchangeRecord"); // 需要转换的entity实体类名称
    private static final String CLASS_SUFFIX = "DTO"; // 类名后缀，哪种类，如dto或者vo
    private static final List<String> JSON_IGNORE_FIELDS = Arrays.asList("createTime", "updateTime", "createBy", "updateBy");
    //endregion

    // 正则匹配字段注释和字段定义 用于匹配java doc注释后接private修饰的成员属性部分
    private static final Pattern FIELD_PATTERN = Pattern.compile("/\\*\\*\\s*\r?\n\\s*\\*\\s*(.*?)\\s*\r?\n\\s*\\*/\\s*\r?\n\\s*private\\s+[\\w\\.<>]+\\s+(\\w+);", Pattern.MULTILINE);


    public static void main(String[] args) {
        String projectRoot = System.getProperty("user.dir"); // 获取当前工作目录（即项目根目录）
        try {
            generateDTOs(projectRoot + SOURCE_DIR, projectRoot + TARGET_DIR, TARGET_PACKAGE_PREFIX);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void generateDTOs(String sourceDir, String targetDir, String packagePrefix) throws IOException {
        Path dir = Paths.get(sourceDir);
        if (!Files.exists(dir)) {
            System.err.println("源目录不存在: " + dir);
            return;
        }

        // 确保输出目录存在
        Path outDir = Paths.get(targetDir);
        if (!Files.exists(outDir)) {
            Files.createDirectories(outDir);
        }

        Files.walk(dir)
                .filter(path -> TARGET_ENTITY_NAMES.stream().anyMatch(name -> path.getFileName().toString().equals(name + ".java")))
                .forEach(path -> processFile(path, outDir, packagePrefix));
    }

    private static void processFile(Path entityPath, Path targetPath, String packagePrefix) {
        try {
            String content = new String(Files.readAllBytes(entityPath));

            // 替换类名 Entity -> DTO
            String className = getClassName(entityPath);
            String newClassName = className + CLASS_SUFFIX;
            String newContent = content.replace(className, newClassName);

            // 替换包路径
            String packageLine = "package " + packagePrefix + ";\n";
            newContent = newContent.replaceFirst("package\\s+[\\w\\.]+;", packageLine);

            // 插入 Lombok 和 Swagger 注解
            newContent = addAnnotations(newContent, packageLine);

            // 随机生成一个serialVersionUID
            newContent = newContent.replace("private static final long serialVersionUID = 1L;", "private static final long serialVersionUID = " + Math.round(Math.random() * 1000000000000000000L) + "L;");

            // 写入新文件
            Path pojoPath = targetPath.resolve(newClassName + ".java");
            Files.write(pojoPath, newContent.getBytes());
            System.out.println("生成成功: " + pojoPath);
        } catch (IOException e) {
            System.err.println("处理文件失败: " + entityPath);
            e.printStackTrace();
        }
    }

    /**
     * 获取类名
     * @param path 文件路径
     */
    private static String getClassName(Path path) {
        String fileName = path.getFileName().toString();
        return fileName.substring(0, fileName.lastIndexOf('.'));
    }

    /**
     * 添加注解
     * @param content 文件内容
     * @param packageLine 指定包名
     */
    private static String addAnnotations(String content, String packageLine) {
        // 添加 import
        String imports = "\nimport lombok.AllArgsConstructor;\n" +
                         "import lombok.NoArgsConstructor;\n" +
                         "import lombok.Builder;\n" +
                         "import io.swagger.annotations.ApiModel;\n" +
                         "import io.swagger.annotations.ApiModelProperty;\n" +
                         "import com.fasterxml.jackson.annotation.JsonInclude;\n" +
                         "import com.fasterxml.jackson.annotation.JsonIgnore;";

        content = content.replace(packageLine, packageLine + imports);

        // 添加 类注解
        content = content.replace("public class", "@AllArgsConstructor\n@NoArgsConstructor\n@ApiModel\n@Builder(toBuilder = true)\n@JsonInclude(JsonInclude.Include.NON_NULL)\npublic class");

        // 添加 @ApiModelProperty 到每个字段
        Matcher matcher = FIELD_PATTERN.matcher(content);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            String comment = matcher.group(1);
            String fieldName = matcher.group(2);
            String replacement;
            if (JSON_IGNORE_FIELDS.contains(fieldName)) {
                replacement = String.format("\n\t@ApiModelProperty(\\\"%s\\\")\n\t@JsonIgnore\n\tprivate %s %s;",
                        comment, extractFieldType(content, fieldName), fieldName);
            } else {
                replacement = String.format("\n\t@ApiModelProperty(\\\"%s\\\")\n\tprivate %s %s;",
                        comment, extractFieldType(content, fieldName), fieldName);
            }
            matcher.appendReplacement(sb, replacement);
        }
        matcher.appendTail(sb);

        return sb.toString();
    }

    /**
     * 提取成员属性类型
     * @param content 文件内容
     * @param fieldName 属性名
     */
    private static String extractFieldType(String content, String fieldName) {
        Pattern pattern = Pattern.compile("private\\s+(\\w+)\\s+" + Pattern.quote(fieldName) + "\\s*;");
        Matcher matcher = pattern.matcher(content);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return "Object";
    }

}
